#include "Dibujante.h"

Dibujante::Dibujante(int ancho, int alto){
	this->vista = SDL_SetVideoMode(ancho,alto, BPP_PANTALLA, SDL_DOUBLEBUF | SDL_HWSURFACE);
}

Dibujante::Dibujante(void)
{
	//Inicializar superficie principal.
	this->vista = SDL_SetVideoMode(ANCHO_PANTALLA, ALTO_PANTALLA, BPP_PANTALLA, SDL_DOUBLEBUF | SDL_HWSURFACE);
}

Dibujante::~Dibujante(void)
{

	//Salir de SDL
	SDL_Quit();
}

void Dibujante::dibujar(ObjetoDibujable *objetoDibujable, Pantalla *pantalla)
{
	//La aplico en pantalla
	this->aplicarSuperficie(
			objetoDibujable->getPosicionX() - pantalla->getPosicion(),
			objetoDibujable->getPosicionY(),
			objetoDibujable->getAncho(),
			objetoDibujable->getAlto(),
			objetoDibujable->getSprite(),
			this->vista
			);
}



void Dibujante::dibujarLista(list<ObjetoDibujable*>& lista, Pantalla *pantalla, bool fondo)
{
	list<ObjetoDibujable*>::const_iterator it;
	for ( it = lista.begin(); it != lista.end(); it++ ) {
		ObjetoDibujable *od = *it;
		if (fondo)
			dibujar(od, pantalla);
		else
			dibujarEnFondo(od);
	}
}

void Dibujante::dibujarEnFondo(ObjetoDibujable *objetoDibujable) {
	int xTapado = 0;
	int yTapado = 0;
	int ancho, alto;
	while ( yTapado < objetoDibujable->getAlto() ) {

		while ( xTapado < objetoDibujable->getAncho() ) {
			ancho = objetoDibujable->getSprite()->w;
			if ( objetoDibujable->getAncho() - xTapado < ancho )
				ancho = objetoDibujable->getAncho() - xTapado;
			alto = objetoDibujable->getSprite()->h;
				if ( objetoDibujable->getAlto() - yTapado < alto )
					alto = objetoDibujable->getAlto() - yTapado;
			this->aplicarSuperficie(
					objetoDibujable->getPosicionX() + xTapado,
					objetoDibujable->getPosicionY() + yTapado,
					ancho,
					alto,
					objetoDibujable->getSprite(),
					this->fondo
					);
			xTapado += objetoDibujable->getSprite()->w;
		}

		yTapado += objetoDibujable->getSprite()->h;
		xTapado = 0;

	}
}

SDL_Surface* Dibujante::getImagenOptimizada( string ruta )
{
	SDL_Surface* imagenCargada = NULL;
	SDL_Surface* imagenOptimizada = NULL;

	imagenCargada = IMG_Load( ruta.c_str() );

	if(imagenCargada != NULL)
	{
		imagenOptimizada = SDL_DisplayFormatAlpha( imagenCargada );
		SDL_FreeSurface( imagenCargada );
	}

	return imagenOptimizada;
}

void Dibujante::aplicarSuperficie(int x, int y, int ancho, int alto, SDL_Surface* superficieOrigen, SDL_Surface* superficieDestino) {
	SDL_Rect source;
	source.x = 0;
	source.y = 0;
	source.w = ancho;
	source.h = alto;

	SDL_Rect offset;
	offset.x = x;
	offset.y = y;


	SDL_BlitSurface( superficieOrigen, &source, superficieDestino, &offset );
}



void Dibujante::resetScreen(Pantalla* pantalla) {
	SDL_Rect source;
	source.x = pantalla->getPosicion();
	source.y = 0;
	source.w = pantalla->getAncho();
	source.h = pantalla->getAlto();

	SDL_BlitSurface( this->fondo, &source, this->vista, NULL );
}

void Dibujante::mostrar() {
	SDL_Flip(this->vista);
}

void DrawPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

Uint32 ReadPixel(SDL_Surface *surface, int x, int y)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to retrieve */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        return *p;
        break;

    case 2:
        return *(Uint16 *)p;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;
        break;

    case 4:
        return *(Uint32 *)p;
        break;

    default:
        return 0;       /* shouldn't happen, but avoids warnings */
    }
}

SDL_Surface *ScaleSurface(SDL_Surface *Surface, Uint16 Width, Uint16 Height)
{
    if(!Surface || !Width || !Height)
        return 0;

    SDL_Surface *_ret = SDL_CreateRGBSurface(Surface->flags, Width, Height, Surface->format->BitsPerPixel,
        Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask);

    double    _stretch_factor_x = (static_cast<double>(Width)  / static_cast<double>(Surface->w)),
        _stretch_factor_y = (static_cast<double>(Height) / static_cast<double>(Surface->h));

    for(Sint32 y = 0; y < Surface->h; y++)
        for(Sint32 x = 0; x < Surface->w; x++)
            for(Sint32 o_y = 0; o_y < _stretch_factor_y; ++o_y)
                for(Sint32 o_x = 0; o_x < _stretch_factor_x; ++o_x)
                    DrawPixel(_ret, static_cast<Sint32>(_stretch_factor_x * x) + o_x,
                        static_cast<Sint32>(_stretch_factor_y * y) + o_y, ReadPixel(Surface, x, y));

    return _ret;
}


void Dibujante::crearFondo(string rutaFondo, int alto, int ancho) {
	this->fondo = getImagenOptimizada(rutaFondo);

	this->fondo = ScaleSurface(this->fondo, ancho, alto);


	if(!this->fondo)
	{
        this->crearFondoEmergente();
	}
}

void Dibujante::crearFondoEmergente()
{

    //Crear una superficie emergente, en caso de falla.
    Uint32 rmask, gmask, bmask, amask;

    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        rmask = 0xff000000;
        gmask = 0x00ff0000;
        bmask = 0x0000ff00;
        amask = 0x00000000;
    #else
        rmask = 0x000000ff;
        gmask = 0x0000ff00;
        bmask = 0x00ff0000;
        amask = 0x00000000;
    #endif

    this->fondo = SDL_CreateRGBSurface(0, ANCHO_NIVEL, ALTO_NIVEL, 32,
                                   rmask, gmask, bmask, amask);

}

void Dibujante::dibujarRotado(ObjetoDibujable *objetoDibujable, SDL_Surface* superficie) {

	int xTapado = 0;
	int yTapado = 0;
	int ancho, alto;
	int angulo= ((Plataforma*)objetoDibujable)->getRotacion();

	int valor=objetoDibujable->getAncho();
	if (objetoDibujable->getAlto()> objetoDibujable->getAncho()) valor= objetoDibujable->getAlto();
	superficie = ScaleSurface(superficie,objetoDibujable->getAncho(),objetoDibujable->getAlto());
//	superficie = ScaleSurface(superficie,valor,valor);
	SDL_SetAlpha (superficie, SDL_SRCALPHA, 255);
	superficie=SDL_DisplayFormat(superficie);
	superficie=SDL_DisplayFormatAlpha(superficie);

	while ( yTapado < objetoDibujable->getAlto() ) {

		while ( xTapado < objetoDibujable->getAncho() ) {
			ancho = objetoDibujable->getSprite()->w;
			if ( objetoDibujable->getAncho() - xTapado < ancho )
				ancho = objetoDibujable->getAncho() - xTapado;
			alto = objetoDibujable->getSprite()->h;
				if ( objetoDibujable->getAlto() - yTapado < alto )
					alto = objetoDibujable->getAlto() - yTapado;

			SDL_Rect source;
			source.x = 0;
			source.y = 0;
			source.w = ancho;
			source.h = alto;

			SDL_Rect offset;
			offset.x = xTapado;
			offset.y = yTapado;


			SDL_BlitSurface( objetoDibujable->getSprite(), &source, superficie, &offset);
			//SDL_BlitSurface( objetoDibujable->getSprite(), &source, transparente, &offset); ---NUEVO


			xTapado += objetoDibujable->getSprite()->w;
		}

		yTapado += objetoDibujable->getSprite()->h;
		xTapado = 0;

	}

	int nuevaPos_x=objetoDibujable->getPosicionX();
	int nuevaPos_y=objetoDibujable->getPosicionY();

	if (angulo!=0){
		//nuevaPos_x+=valor/2;
		//nuevaPos_y-= valor/2*cos(angulo/180*3.1415);
		nuevaPos_y= nuevaPos_y - valor/2 - (objetoDibujable->getSprite()->h*sin(angulo));
		cout<<nuevaPos_x<<";"<<nuevaPos_y<<";"<<objetoDibujable->getPosicionX()<<";"<<objetoDibujable->getPosicionY()<<endl;
	}

	SDL_Surface* superficieRotada= rotozoomSurface(superficie,angulo,1.0,0);
	SDL_SetColorKey(superficieRotada,SDL_SRCCOLORKEY,SDL_MapRGB(superficieRotada->format,255,0,255));
	this->aplicarSuperficie(nuevaPos_x,nuevaPos_y,valor,valor,superficieRotada,this->fondo);
}

